#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map>

#include "Socket.hpp"
// #include "Log.hpp"

const std::string sep = "\r\n";
const std::string webroot = "./webroot";    //web根目录
const std::string homepage = "index.html";  // 主页面

class HttpServer;
class ThreadData
{
public:
    ThreadData(int fd, HttpServer* s)
        :sockfd(fd)
        ,svr(s)
    {}
public:
    int sockfd;
    HttpServer *svr;
};


// Http请求
class HttpRequest
{
public:
    void Deserialize(std::string req)   // 反序列化（报头+正文）
    {
        while(true)
        {
            std::size_t pos = req.find(sep);
            if(pos == std::string::npos) break;
            std::string tmp = req.substr(0, pos);
            if(tmp.empty()) break;
            req_header.push_back(tmp);
            req.erase(0, pos+sep.size());
        }
        text = req;
    }

    void Parse()    // 报头第一行分析解析，Web网页
    {
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;

        file_path = webroot;    // ./webroot
        if(url == "/" || url == "/index.html")
        {
            file_path += "/";
            file_path += homepage;  // ./webroot/index.html
        }
        else    
            file_path += url;       // ./webroot/a/b/c/d.html
        
        auto pos = file_path.rfind(".");
        if(pos == std::string::npos)    suffix = ".html";
        else    suffix = file_path.substr(pos);

    }

    void DebugPrint()
    {
        for(auto& line : req_header)
        {
            std::cout << "---------------------------" << std::endl;
            std::cout << line << std::endl << std::endl;
        }

        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;

        std::cout << text << std::endl;
    }
public:
    std::vector<std::string> req_header;    //报文，每行
    std::string text;               // 正文

    // 解析之后
    std::string method;
    std::string url;
    std::string http_version;   //询问状态描述

    std::string file_path;      //网页文件路径
    std::string suffix;         // 网页后缀
};

class HttpServer
{
public:
    HttpServer(uint16_t port)
        :port_(port)
    {
        content_type.insert({"./html", "text/html"});
        content_type.insert({".png", "image/png"});
    }
    ~HttpServer(){}
public:
    void Start()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        // 套接字socket部署完成
        while(true)
        {
            std::string clientip;   //获取谁连接的我
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);    //服务端accept接收
            if(sockfd < 0)  continue;
            // lg(Info, "get a new connect, sockfd: %d", sockfd);

            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        std::ifstream in(htmlpath, std::ios::binary);
        if(!in.is_open())   return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::end);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());
        in.close();
        return content;
    }

    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type["./html"];
        else return content_type[suffix];
    }

    void HttpRun(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer)-1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "bufffer: " << buffer << std::endl;
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();
            req.DebugPrint();

            // 返回响应
            std::string text;
            bool ok = true;
            text = ReadHtmlContent(req.file_path);
            if(text.empty())
            {
                std::cout <<"---------?" << std::endl;
                ok = false;
                std::string err_html = webroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }

            std::string response_line;
            if(ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else 
                response_line = "HTTP/1.0 404 Not Found\r\n";
            
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "Set-Cookie: name=zsl&&passwd=123445";
            response_header += "\r\n";

            std::string blank_line = "\r\n";
            std::string response = response_line;
            response_line += blank_line;
            response_line += text;

            std::cout << "===============" << std::endl;
            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }

    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        td->svr->HttpRun(td->sockfd);
        delete td;
        return nullptr;
    }
private:
    Sock listensock_;
    uint16_t port_;

    std::unordered_map<std::string, std::string> content_type;
};